#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include "Err.hpp"
#include "Log.hpp"
#include "Util.hpp"
#include <unistd.h>
#include "Protocol.hpp"
#include "Connect.hpp"
#include "Epoll.hpp"

using namespace std;

class Tcpclient
{
public:
    // 构造
    Tcpclient(const string &serverip, const uint16_t &serverport)
        : _sock(-1), _serverip(serverip), _serverport(serverport)
    {
    }

    // 析构
    ~Tcpclient()
    {
        if (_sock >= 0)
            close(_sock);
    }

    // 初始化
    void Init()
    {
        // 1、创建套接字
        Create();
        log(DEBUG, "socket create success, sock:%d", _sock);
        // 2、建立连接
        Connect_();
        log(DEBUG, "connect success");
    }

    // 启动
    void start()
    {
        char commandline[1024]; // 命令行缓冲区
        while (true)
        {
            // 1、从键盘阻塞式读取数据
            int n = read(0, commandline, sizeof(commandline) - 1);

            if (n > 0) // 读取成功
            {
                commandline[n - 1] = 0; // 将最后的\n置为0
                // 2、给正文添加报头，形成一个完整的请求报文
                string text = commandline;      // 正文
                string reqstr = enlength(text); // 完整请求
                //cout << reqstr << endl;

                // 3、阻塞式发送请求报文
                int s = send(_sock, reqstr.c_str(), reqstr.size(), 0);
                if (n < 0)
                {
                    log(FATAL, "send failed");
                    exit(SEND_FAILED);
                }

                // 4、阻塞式接收响应
                char buffer[1024];
                s = recv(_sock, buffer, sizeof(buffer) - 1, 0);
                if (s < 0)
                {
                    log(FATAL, "recv failed");
                    exit(RECV_FAILED);
                }
                else if (s == 0)
                {
                    log(DEBUG, "server quit,me too");
                    exit(RECV_FAILED);
                }

                // 接收成功，在最后一个有效字符后添加一个\0
                buffer[s] = 0;
                // 将收到的数据追加到接收缓冲区
                _inbuffer += buffer;
                // 处理接收缓冲区中的响应报文
                hander_responds();
            }
            else if (n == 0) // strio已关闭
            {
                log(ERROR, "stdio is closed");
                break;
            }
            else // 读取出错
            {
                log(ERROR, "read from stdio failed");
                exit(READ_ERR);
            }
        }
    }

private:
    void Create()
    {
        // 创建套接字
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            log(FATAL, "socket create failed");
            exit(SOCK_CREATE_FAILED);
        }
    }

    // 建立链接
    void Connect_()
    {
        struct sockaddr_in server; // 服务器属性结构
        socklen_t len = sizeof(server);
        bzero(&server, sizeof(server));
        server.sin_addr.s_addr = inet_addr(_serverip.c_str()); // 将点分十进制ip地址转化成大端字节序的整型
        server.sin_port = htons(_serverport);                  // 将本地端口号转化成网络大端字节序
        server.sin_family = AF_INET;                           // 通信方式

        if (connect(_sock, (struct sockaddr *)&server, len) < 0) // 建立失败
        {
            log(FATAL, "connect faied");
            exit(SOCK_CONNECT_FAILED);
        }
    }

    void hander_responds()
    {
        int start = 0; // 下一个响应报文的起始位置
        while (true)   // 处理所有的请求报文
        {
            // 1、先寻找请求报文中的行分隔符
            size_t pos = _inbuffer.find(LINE_SEP, start);
            if (pos == string::npos) // 不存在行分隔符
            {
                log(DEBUG, "no more respond_1");
                break;
            }

            // 2、找到了行分隔符，提取行分隔符之前的正文长度，然后计算当前接收缓冲区中是否有一个完整报文
            string textlen_str = _inbuffer.substr(start, pos - start); // 正文长度(字符串形式)
            int textlen = stoi(textlen_str);                                 // 正文长度(整型形式)
            int resp_len = textlen_str.size() + 2 * LEN_LINE_SEP + textlen;   // 响应长度长度

            if (_inbuffer.size() - start < resp_len) // 剩余部分没有一个完整的请求
            {
                log(DEBUG, "no more respond_2");
                break;
            }

            // 3、剩余部分还有完整请求，截取请求中的正文部分
            string resp_text = _inbuffer.substr(pos + LEN_LINE_SEP, textlen); // 请求正文
            start += resp_len;
            cout<< "get a respond:" << resp_text << endl;
        }
        _inbuffer.erase(0, start);//移除start之前的数据
    }

private:
    Tcpclient(const Tcpclient &) = delete;            // 删除拷贝构造
    Tcpclient &operator=(const Tcpclient &) = delete; // 删除赋值重载

private:
    int _sock; // 文件描述符
    string _inbuffer;
    string _serverip;     // 服务器ip
    uint16_t _serverport; // 服务器端口号
};
